package com.vincent.template.commons.base;

import com.google.common.collect.Lists;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Vincent Wang
 * @date 2021/3/26 10:02
 */
public class BaseJpaRepositoryImpl<T, K extends Serializable>
        extends SimpleJpaRepository<T, K>
        implements BaseJpaRepository<T, K> {

    private static final String QUERY_ID_TEMPLATE = "select id from %s";
    private static final String ID_IN_CONDITION = " where id in %s";
    private static final String NOT_DELETED_CONDITION = " and isDeleted = 0";

    private final EntityManager em;
    private final Class<T> domainClass;
    private final Class<K> idClass;
    private final String tableName;

    public BaseJpaRepositoryImpl(Class<T> domainClass, Class<K> idClass, EntityManager em) {
        super(domainClass, em);
        this.em = em;
        this.domainClass = domainClass;
        this.idClass = idClass;
        this.tableName = domainClass.getAnnotation(Table.class).name();
    }

    @Override
    public List<K> findAllIds() {
        return queryIds(null);
    }

    @Override
    public List<K> findExistingIds(Iterable<K> ids) {
        if (!ids.iterator().hasNext()) {
            return Lists.newArrayList();
        }
        Separator sep = String.class.equals(idClass) ? Separator.STRING : Separator.NUMBER;
        String idStr = Lists.newArrayList(ids).stream()
                .map(String::valueOf).collect(Collectors
                        .joining(sep.getDelimiter(), sep.getPrefix(), sep.getSuffix()));
        String condition = String.format(ID_IN_CONDITION, idStr);
        if (BaseFieldPart.class.isAssignableFrom(domainClass)) {
            condition += NOT_DELETED_CONDITION;
        }
        return queryIds(condition);
    }

    @Override
    public List<K> findNotExistingIds(Iterable<K> ids) {
        if (!ids.iterator().hasNext()) {
            return Lists.newArrayList();
        }
        return ListUtils.subtract(Lists.newArrayList(ids), findExistingIds(ids));
    }

    @Override
    public int batchSave(Iterable<T> entities) {
        if (!entities.iterator().hasNext()) {
            return 0;
        }
        return 1;
    }

    public List<K> queryIds(String condition) {
        String sql = String.format(QUERY_ID_TEMPLATE, tableName);
        if (StringUtils.isNotBlank(condition)) {
            sql += condition;
        }
        Query query = em.createNativeQuery(sql);
        List<K> queryList = query.getResultList();
        if (String.class.equals(idClass)) {
            return queryList;
        }
        return queryList.stream()
                .map(e -> (K) Long.valueOf(e.toString()))
                .collect(Collectors.toList());
    }

    /**
     * sql中in条件的分隔符
     */
    private enum Separator {

        /**
         * 字符串类型
         */
        STRING("','", "('", "')"),
        /**
         * 数值类型
         */
        NUMBER(",", "(", ")");

        private final String delimiter;

        private final String prefix;

        private final String suffix;

        Separator(String delimiter, String prefix, String suffix) {
            this.delimiter = delimiter;
            this.prefix = prefix;
            this.suffix = suffix;
        }

        public String getDelimiter() {
            return delimiter;
        }

        public String getPrefix() {
            return prefix;
        }

        public String getSuffix() {
            return suffix;
        }
    }
}
